import { any } from "../core/logic";
import { isBlank } from "../core/utils";
import { Component, ComponentType } from "./Component";
import { Quaternion } from '../core/math/Quaternion.js';
import { Vector3 } from '../core/math/Vector3.js';
import { Matrix4 } from '../core/math/Matrix4.js';
import { Euler } from '../core/math/Euler.js';
import { Buffer } from "../gpu/types/Buffer.js";
import { Register } from "../register/Register";
import {  Engine} from "../Engine";
import { TransInstance } from "./TransInstance.js";
const _v1 = new Vector3();
const _q1 = new Quaternion();
const _m1 = new Matrix4();
const _target = new Vector3();
const _xAxis = new Vector3(1, 0, 0);
const _yAxis = new Vector3(0, 1, 0);
const _zAxis = new Vector3(0, 0, 1);
const DefaultUp = new Vector3(0, 1, 0);
/**
 * Trans组件，提供物体的位置信息。
 * @class
 * @extends w.component.Component
 * @memberof w.component
 */
class Trans extends Component {
    /**
     * @constructor
     * @param {Array} opts.position 【可选,缺省:[0,0,0]】位置信息。
     * @param {Array} opts.sclae 【可选,缺省:[1,1,1]】缩放信息。
     * @param {Array} opts.rotation 【可选,缺省:[0,0,0]】旋转信息。
     * @param {*} share 
     */
    constructor(opts = {}, share) {
        opts.type = any(opts.type, ComponentType.Trans);
        super(opts, share);
        this.hash="trans";
        this.up = DefaultUp.clone();
        this._position = new Vector3();
        this._rotation = new Euler();
        this._scale = new Vector3(1,1,1);
        this._quaternion=new Quaternion();
        this._rotation._onChange(this.onRotationChange.bind(this));
        this._quaternion._onChange(this.onQuaternionChange.bind(this));
        this._matrix = new Matrix4();
        this._matrixWorld = new Matrix4();
        this._normalMat=new Matrix4();
        if (opts.position) {
            this._position = this._position.fromArray(opts.position);
        }
        if (opts.rotation) {
            this._rotation = this._rotation.fromArray(opts.rotation);
        }
        if (opts.scale) {
            this._scale = this._scale.fromArray(opts.scale);
        }
        if (opts.matrix) {
            this.applyMatrix4(opts.matrix);
        }
        this.addConfigParam("instanceCount", 1);
        this.addParam("trans");
        this.pickId = null;
        this.autoUpdate=true;
        this.update();
    }
    get normalMat(){
        return this._normalMat;
    }
    updateState(){
        this.setState(true);
    }
    get pickId(){
        return this._pickId;
    }
    set pickId(v){
       if(isBlank(v))return 
        this._pickId = v;
        this.addParam("pickId");
        this.update();
        return this;
    }
    set position(v){
        if(Array.isArray(v)){
            this._position.fromArray(v);
        }else{
            this._position=v;
        }
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }
    get position(){
        return this._position;
    }
    set scale(v){
        if(Array.isArray(v)){
            this._scale.fromArray(v);
        }else{
            this._scale=v;
        }
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }
    get scale(){
        return this._scale;
    }
    set rotation(v){
        if(Array.isArray(v)){
            this._rotation.fromArray(v);
        }else{
            this._rotation=v;
        }
        
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }
    get rotation(){
        return this._rotation;
    }
    set quaternion(v){
        if(Array.isArray(v)){
            this._quaternion.fromArray(v);
        }else{
            this._quaternion=v;
        }
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }
    get quaternion(){
        return this._quaternion;
    }
    dis(position){
       return Math.sqrt((this.position.x-position[0])*(this.position.x-position[0])+(this.position.y-position[1])*(this.position.y-position[1])+(this.position.z-position[2])*(this.position.z-position[2]));
    }
    onRotationChange() {
        this.quaternion.setFromEuler(this.rotation, false);
    }
    onQuaternionChange() {
        this.rotation.setFromQuaternion(this.quaternion, undefined, false);
    }
    applyMatrix4(matrix) {
        this._matrix.compose(this.position, this.quaternion, this.scale);
        this.matrix.multiply(matrix);
        this.matrix.decompose(this.position, this.quaternion, this.scale);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }
    preApplyMatrix4(matrix) {
        this._matrix.compose(this.position, this.quaternion, this.scale);
        this.matrix.premultiply(matrix);
        this.matrix.decompose(this.position, this.quaternion, this.scale);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }
    applyQuaternion(q) {
        this.quaternion.multiply(q);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }

    setRotationFromAxisAngle(axis, angle) {
        // assumes axis is normalized
        this.quaternion.setFromAxisAngle(axis, angle);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }

    setRotationFromEuler(euler) {
        this.quaternion.setFromEuler(euler, true);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }
    
    setRotationFromMatrix(m) {
        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
        this.quaternion.setFromRotationMatrix(m);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }

    setRotationFromQuaternion(q) {
        // assumes q is normalized
        this.quaternion.copy(q);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
    }

    rotateOnAxis(axis, angle) {
        // rotate object on axis in object space
        // axis is assumed to be normalized
        _q1.setFromAxisAngle(axis, angle);
        this.quaternion.multiply(_q1);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }

    rotateOnWorldAxis(axis, angle) {
        // rotate object on axis in world space
        // axis is assumed to be normalized
        // method assumes no rotated parent
        _q1.setFromAxisAngle(axis, angle);
        this.quaternion.multiply(_q1);
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }
    rotateXWorld(angle){
        return this.rotateOnWorldAxis(_xAxis, angle);
    }
    rotateYWorld(angle){
        return this.rotateOnWorldAxis(_yAxis, angle);
    }
    rotateZWorld(angle){
        return this.rotateOnWorldAxis(_zAxis, angle);
    }

    rotateX(angle) {
        return this.rotateOnAxis(_xAxis, angle);
    }

    rotateY(angle) {
        return this.rotateOnAxis(_yAxis, angle);
    }

    rotateZ(angle) {
        return this.rotateOnAxis(_zAxis, angle);
    }

    translateOnAxis(axis, distance) {
        // translate object by distance along axis in object space
        // axis is assumed to be normalized
        _v1.copy(axis).applyQuaternion(this.quaternion);
        this.position.add(_v1.multiplyScalar(distance));
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
        return this;
    }

    translateX(distance) {
        return this.translateOnAxis(_xAxis, distance);
    }

    translateY(distance) {
        return this.translateOnAxis(_yAxis, distance);
    }

    translateZ(distance) {
        return this.translateOnAxis(_zAxis, distance);
    }
    getWorldDirection( target ) {
		const e = this.matrixWorld.elements;
		return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();
	}
	lookAt(position) {
		// This method does not support objects having non-uniformly-scaled parent(s)
        if(Array.isArray(position)){
            if (position.length < 3) {
                position[2] = 0;
            }
            this._position.fromArray(position);
        }
		_m1.lookAt( this.position, _target, this.up );//-z方向作为lookAt点（物体约定如此，相机也约定如此）
		this.quaternion.setFromRotationMatrix( _m1 );
        this.setState(false,"matrix");
        this.addParam("trans");
        this.update();
	}
    get instanceCount(){
        return 1;
    }
    toGPU(meshCom){
        let transParam=this.getParam("trans");
        if(!transParam){
            let data=new Float32Array([...this.matrixWorld.elements,...this.normalMat.elements]);
            transParam = new Buffer({
                name:"trans",
                usage:GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
                visibility: GPUShaderStage.VERTEX,
                type: "uniform",
                size: 32 * 4,
                data
            });
            this.addParam("trans",transParam);
            transParam.state = true;
            data=null;//销毁
        } else {
            if(!transParam.state) {
                let data=new Float32Array([...this.matrixWorld.elements,...this.normalMat.elements]);
                transParam.buffer(data);
                transParam.state = true;
                data=null;//销毁
            }
        }
        if (!this.grouplayouts1) {
            const params = this.getAllParams()
            let entriesGroup1 = [], entriesLayout1 = []
            this.defs = {}
            this.config={}
            for (let index = 0; index < params.length; index++) {
              const param = params[index];
              this.defs[param.name] = param.value;
              if(param.group===1){
                entriesGroup1.push(param.gpu.entry)
                entriesLayout1.push(param.gpu.layout)
              }else{
                this.config[param.name]=param.value
              }
            }
            if(meshCom.geometryEntriesGroup)entriesGroup1.push(...meshCom.geometryEntriesGroup)
            if(meshCom.geometrEntriesLayout)entriesLayout1.push(...meshCom.geometrEntriesLayout)
            this.grouplayouts1 = Engine.instance.device.createBindGroupLayout({
              label: "layout" + 1,
              entries: entriesLayout1
            });
            this.bindGroup1 = Engine.instance.device.createBindGroup({
              label: "bindGroup" + 1,
              layout: this.grouplayouts1,
              entries: entriesGroup1
            });
          }
        
    }
    get distance(){
        return this.position.distanceTo(_target);
    }
    localToWorld(vector) {
        return vector.applyMatrix4(this.matrixWorld);
    }

    worldToLocal(vector) {
        return vector.applyMatrix4(_m1.copy(this.matrixWorld).invert());
    }

    get matrix() {
        if (!this.getState("matrix")) {
            this.updateMatrix();
        }
        return this._matrix;
    }
    get matrixWorld() {
        if (!this.getState("matrix")) {
            this.updateMatrix();
        }
        return this._matrixWorld;
    }
    /**
     * 获取他的上级Trans组件
     */
    get parent() {
        let parentTrans=null;
        this.eids.forEach(id=>{
            const entity = Register.instance.entityManager.get(id);
            if (entity.parent) {
                const parent = Register.instance.entityManager.get(entity.parent);
                if (parent) {
                    parentTrans=parent.getComponent(ComponentType.Trans);
                }
            }
        })
        return parentTrans;
    }
    get children() {
        let children = [];
        this.eids.forEach(id=>{
            const entity = Register.instance.entityManager.get(id);
            for (let i = 0; i < entity.children.length; i++) {
                const child = Register.instance.entityManager.get(entity.children[i]);
                if (child) {
                    const trans = child.getComponent(ComponentType.Trans);
                    if (trans) {
                        children.push(trans);
                    }
                }
            }
        })
        return children;
    }

    updateMatrix() {
        this._matrix.compose(this.position, this.quaternion, this.scale);
        this.updateMatrixWorld();
        this.setState(true,"matrix");   
    }

    updateMatrixWorld() {
        if (this.parent === null) {
            this._matrixWorld.copy(this._matrix);
        } else {
            this._matrixWorld.multiplyMatrices(this.parent.matrixWorld, this._matrix);
        }
        this._normalMat=this._matrixWorld.clone().invert().transpose();
        // update children
        // const children = this.children;
        // for (let i = 0, l = children.length; i < l; i++) {
        //     children[i].setState(false);
        // }
    }
    updateHash () {
        let hash = '';
        this.getAll().forEach(([paramsId, param]) => {
            if(!(param && param.isConfig)) {
                hash = hash + paramsId;
            }
        })
       
        this.hash = hash;
    }
    update(){
        if(this.instanceTrans){
            this.instanceTrans.itemUpdate();
        }
        this.transerUpdate();
        super.update();
    }
    transerUpdate(){
        this.eids.forEach(id=>{
            const entity = Register.instance.entityManager.get(id);
            if(entity){
                for (let i = 0; i < entity.children.length; i++) {
                    const child = Register.instance.entityManager.get(entity.children[i]);
                    if (child) {
                        const trans = child.getComponent(ComponentType.Trans);
                        if (trans) {
                            if (!(trans instanceof TransInstance)) {
                              trans.setState(false,"matrix"); 
                              trans.addParam("trans");
                            } 
                            trans.transerUpdate();//递归更新
                        }
                    }
                }
            }
            
        })
    }
    toString(){
        this.default={
            position:this.position.toArray(),
            rotation:this.rotation.toArray(),
            scale:this.scale.toArray()
            
        } 
    }
}
export { Trans }